<?php
/**
 * Main Pants namespace. Contains static methods used throughout the
 * application.
 *
 * @author Ryan Grove <ryan@wonko.com>
 * @copyright Copyright (c) 2007 Ryan Grove. All rights reserved.
 * @license http://opensource.org/licenses/bsd-license.php New BSD License
 * @abstract
 * @static
 */
abstract class Pants {
  public static $db;
  public static $parameters;

  /**
   * Initializes Pants. This method should only be called once from
   * {@link common.php common.php}
   */
  public static function init() {
    // Open database connection.
    self::$db = new PantsDB(DB_HOST, DB_USER, DB_PASS, DB_NAME);

    // Set page parameters.
    self::$parameters = array(
      'admin'            => self::auth(),
      'adminUrl'         => SITE_URL.'/pants/admin',
      'dateFormatAtom'   => 'c',
      'dateFormatLong'   => 'l F jS, Y @ h:i A (T)',
      'dateFormatRSS'    => 'r',
      'dateFormatShort'  => 'Y-m-d H:i',
      'dateFormatTime'   => 'h:i A (T)',
      'pageUrl'          => PAGE_URL,
      'siteName'         => SITE_NAME,
      'siteDescription'  => SITE_DESCRIPTION,
      'siteAuthor'       => SITE_AUTHOR,
      'siteEmail'        => SITE_EMAIL,
      'siteUrl'          => SITE_URL,
      'siteRelativeUrl'  => rtrim(SITE_RELATIVE_URL, '/'),
      'themeDir'         => PANTS_DIR.'/themes/'.PANTS_THEME,
      'themeRelativeUrl' => rtrim(SITE_RELATIVE_URL, '/').'/pants/themes/'.PANTS_THEME,
      'themeUrl'         => SITE_URL.'/pants/themes/'.PANTS_THEME,
    );
  }

  /**
   * Returns a boolean indicating whether or not the current user is logged in
   * as an administrator.
   *
   * @return bool
   */
  public static function auth() {
    return self::getSession('pantsKey') === sha1(ADMIN_USER.PANTS_DIR.
        ADMIN_PASS);
  }

  /**
   * Stops execution and displays a Pants error page.
   *
   * @param string $title error page title
   * @param string $message error message
   * @param string $type page output type
   */
  public static function error($title, $message, $type = 'html') {
    Pants::render('error', array(
      'pageTitle'    => $title,
      'errorTitle'   => $title,
      'errorMessage' => $message,
    ), $type);
  }
  
  /**
   * Stops execution and displays a Pants error page with an HTTP 404 Not Found
   * error message.
   *
   * @param string $type page output type
   */
  public static function error404($type = 'html') {
    header('HTTP/1.0 404 Not Found');

    Pants::render('error', array(
      'pageTitle'    => '404 Not Found',
      'errorTitle'   => 'Not Found',
      'errorMessage' => 'The requested URL '.$_SERVER['REQUEST_URI'].
          ' was not found on this server.',
    ), $type);
  }

  /**
   * Uses Cal Henderson's lib_filter to filter non-allowed tags from user-
   * provided HTML.
   *
   * @param string $html unclean html
   * @return string clean html
   */
  public static function filterHTML($html) {
    require 'pants/3rdparty/lib_filter.php';

    $filter = new lib_filter();

    return $filter->go($html);
  }

  /**
   * Gets the value of the cookie with the specified name. If no such cookie
   * exists, returns the specified default value.
   *
   * @param string $name cookie name
   * @param mixed $default default value
   * @return mixed cookie value, or default value if cookie doesn't exist
   */
  public static function getCookie($name, $default = null) {
    return isset($_COOKIE[$name]) ? $_COOKIE[$name] : $default;
  }

  /**
   * Gets the value of the $_POST or $_GET response with the specified name. If
   * no such response field exists, returns the specified default value. $_POST
   * responses take precedence over $_GET responses.
   *
   * @param string $name response field name
   * @param mixed $default default value
   * @return mixed response value, or default value if response field doesn't
   *   exist.
   */
  public static function getResponse($name, $default = null) {
    if (isset($_POST[$name])) {
      return $_POST[$name];
    }

    if (isset($_GET[$name])) {
      return $_GET[$name];
    }

    return $default;
  }

  /**
   * Gets the value of the specified session variable. If no such variable
   * exists, returns the specified default value.
   *
   * @param string $name session variable name
   * @param mixed $default default value
   * @return mixed session variable value, or default value if variable doesn't
   *  exist.
   */
  public static function getSession($name, $default = null) {
    @session_start();
    return isset($_SESSION[$name]) ? $_SESSION[$name] : $default;
  }
  
  public static function linkTo($name, $path = '', $attributes = array()) {
    $link = '<a href="'.SITE_URL.'/'.ltrim($path, '/').'"';
    
    foreach ($attributes as $attr => $value) {
      $link .= ' '.$attr.'="'.htmlentities($value).'"';
    }
    
    echo $link.'>'.$name.'</a>';
  }
  
  /**
   * Handles an administrator login, checking the login form input and setting
   * the appropriate session variables if the login is successful.
   *
   * @return bool true if login is successful
   */
  public static function login() {
    if (self::getResponse('username') != ADMIN_USER ||
        self::getResponse('password') != ADMIN_PASS) {
      throw new PantsLoginException('Invalid username or password.');
    }

    @session_start();
    $_SESSION['pantsKey'] = sha1(ADMIN_USER.PANTS_DIR.ADMIN_PASS);
    
    return true;
  }
  
  /**
   * Logs an administrator out, unsetting the appropriate session variables.
   *
   * @return bool always returns true
   */
  public static function logout() {
    if (self::getSession('pantsKey')) {
      unset($_SESSION['pantsKey']);
    }
    
    return true;
  }

  /**
   * Parses content for infoboxes.
   *
   * @deprecated
   * @param string $content content to be parsed
   * @return string parsed content containing infobox markup
   */
  public static function parseInfoboxes($content) {
    return preg_replace('/\[(.+?)\|(.+?)\]/s', '<span class="infoLink" '.
      'onmouseover="Pants.showInfoBox(this.nextSibling)" '.
      'onmouseout="Pants.hideInfoBox(this.nextSibling)">$2</span>'.
      '<span class="infoBox"><span class="infoBoxCompat"> '.
      '(</span>$1<span class="infoBoxCompat">)</span></span>',
      $content);
  }
  
  public static function partial($pageName, $data = array()) {
    $default = PANTS_DIR.'/themes/default/'.$pageName.'.php';
    $skin    = PANTS_DIR.'/themes/'.PANTS_THEME.'/'.$pageName.'.php';
  
    extract(array_merge($data, self::$parameters));

    if (file_exists($skin)) {
      require $skin;
    } else {
      require $default;
    }
  }

  /**
   * Executes a named database query using the specified parameters and returns
   * the result.
   *
   * @param string $name query name
   * @param array $parameters query parameters
   * @return mixed query result
   */
  public static function query($name, $parameters = array()) {
    return self::$db->query($name, $parameters);
  }

  /**
   * Renders the specified Pants page and sends it to the browser, ending
   * execution.
   *
   * @param string $pageName name of the page to render
   * @param array $data page data to be passed to the page
   * @param string $outputType page output type ('html', 'atom', or 'rss')
   */
  public static function render($pageName = 'index', $data = array(),
      $outputType = null) {

    if (!$outputType) {
      $outputType = self::getResponse('type', 'html');
    }

    self::$parameters = array_merge($data, self::$parameters);
    
    self::$parameters['pageName']  = $pageName;
    self::$parameters['userAgent'] = $_SERVER['HTTP_USER_AGENT'];

    extract(self::$parameters);

    switch($outputType) {
      case 'html':
        header('Content-type: text/html;charset=utf-8');
        $defaultSkin = PANTS_DIR.'/themes/default/'.$pageName.'.php';
        $skin        = PANTS_DIR.'/themes/'.PANTS_THEME.'/'.$pageName.'.php';
        break;

      case 'atom':
        header('Content-type: text/xml;charset=utf-8');
        $defaultSkin = PANTS_DIR.'/themes/default/'.$pageName.'.atom.php';
        $skin        = PANTS_DIR.'/themes/'.PANTS_THEME.'/'.$pageName.'.atom.php';
        break;

      case 'rss':
        header('Content-type: text/xml;charset=utf-8');
        $defaultSkin = PANTS_DIR.'/themes/default/'.$pageName.'.rss.php';
        $skin        = PANTS_DIR.'/themes/'.PANTS_THEME.'/'.$pageName.'.rss.php';
        break;
    }

    if (file_exists($skin)) {
      require $skin;
    } else if (file_exists($defaultSkin)) {
      require $defaultSkin;
    } else {
      self::error404();
    }

    exit;
  }
  
  public static function style($stylesheet, $media = null) {
    $tag = array('<link rel="stylesheet" type="text/css"');
    
    if (isset($media)) {
      $tag[] = ' media="'.$media.'"';
    }
  
    if (file_exists(PANTS_DIR.'/themes/'.PANTS_THEME.'/css/'.$stylesheet)) {
      $tag[] = ' href="'.SITE_URL.'/pants/themes/'.PANTS_THEME.'/css/'.
          $stylesheet.'" />';
    } else {
      $tag[] = ' href="'.SITE_URL.'/pants/themes/default/css/'.
          $stylesheet.'" />';
    }
    
    echo implode('', $tag);
  }
}
?>